﻿using System;
using System.Collections.Generic;
using System.Common.Highliter;
using System.Common.Lock;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;

namespace System.Controls.Highliter
{
  [TemplatePart( Name = "PART_flow_document", Type = typeof( FlowDocument ) )]
  public class SqlTextViewer : Control
  {
    #region ... constructors...

    static SqlTextViewer()
    {
      DefaultStyleKeyProperty.OverrideMetadata( typeof( SqlTextViewer ),
        new FrameworkPropertyMetadata( typeof( SqlTextViewer ) ) );
    }

    #endregion

    public override void OnApplyTemplate()
    {
      base.OnApplyTemplate();

      fDocument = GetTemplateChild( "PART_flow_document" ) as FlowDocument;

      UpdateDocument( SqlText );
    }

    #region ... properties indexers ...

    public string SqlText
    {
      get { return (string)GetValue( SqlTextProperty ); }
      set { SetValue( SqlTextProperty, value ); }
    }

    public bool IsRendering
    {
      get { return (bool)GetValue( IsRenderingProperty ); }
      set { SetValue( IsRenderingProperty, value ); }
    }

    #endregion

    #region ...protected methods...

    protected virtual void OnRenderingBegin()
    {
      var handler = RenderingBegin;
      if ( handler != null )
        handler( this, EventArgs.Empty );
    }

    protected virtual void OnRenderingDone()
    {
      var handler = RenderingDone;
      if ( handler != null )
        handler( this, EventArgs.Empty );
    }

    #endregion

    #region ...private methods...

    private void ClearDocument()
    {
      if ( fDocument == null )
        return;

      if ( fTextChanged.Suppressed )
        return;

      using ( fTextChanged.Suppress() )
        fDocument.Blocks.Clear();
    }

    private static void IsRenderingChanged( DependencyObject dob, DependencyPropertyChangedEventArgs args )
    {
      var new_value = (bool)args.NewValue;

      if ( new_value )
        ( (SqlTextViewer)dob ).OnRenderingBegin();
      else
        ( (SqlTextViewer)dob ).OnRenderingDone();
    }

    private void SqlTextChanged( DependencyPropertyChangedEventArgs args )
    {
      UpdateDocument( (string)args.NewValue );
    }

    private void UpdateDocument( string sqlText )
    {
      if ( fTextChanged.Suppressed )
        return;

      if ( fDocument == null )
        return;

      ClearDocument();
      if ( string.IsNullOrEmpty( sqlText ) )
        return;

      using ( fTextChanged.Suppress() )
      {
        IsRendering = true;
        try
        {
          HighlightStyle.SetDefaults( FontFamily.Source, FontSize, FontStyle, FontWeight, Background, Foreground );

          var rtf_b = new RtfBuilder();
          var rtf = rtf_b.GetRtfContent( sqlText, fFormatter );

          using ( var ms = new MemoryStream( Encoding.GetEncoding( 1250 ).GetBytes( rtf ) ) )
          {
            var range = new TextRange( fDocument.ContentStart, fDocument.ContentEnd );
            range.Load( ms, DataFormats.Rtf );
          }
        }
        finally
        {
          IsRendering = false;
        }
      }
    }

    #endregion

    #region ...events, eventhandlers...

    public event EventHandler RenderingBegin;

    public event EventHandler RenderingDone;

    #endregion

    #region ... fields ...

    private readonly ISqlFormatProvider fFormatter = new FirebirdSqlFormatProvider();
    private readonly Suppressor fTextChanged = new Suppressor();

    private FlowDocument fDocument;

    #endregion

    public static readonly DependencyProperty IsRenderingProperty =
      DependencyProperty.Register( "IsRendering",
        typeof( bool ), typeof( SqlTextViewer ),
        new PropertyMetadata( false, IsRenderingChanged ) );

    public static readonly DependencyProperty SqlTextProperty =
      DependencyProperty.Register( "SqlText",
        typeof( string ), typeof( SqlTextViewer ),
        new PropertyMetadata( string.Empty,
          ( sender, args )=>( (SqlTextViewer)sender ).SqlTextChanged( args ) ) );
  }
}
